#include "GSharpStruct.h"
#include "FieldValuesContainer.h"
#include "GSharpPropertyList.h"
#include "Reflection/FunctionList.h"
#include "Reflection/Function.h"
#include "Reflection/PropertyList.h"
#include "Reflection/GarbageCollectable.h"
#include "GSharpFieldProperty.h"
#include "Object/Object.h"

CGSharpStruct::CGSharpStruct(
    const STypeID &InTypeID
    ,ETypeFlags InExtraFlags
    )
: CType(
    InTypeID
    ,InExtraFlags | ETypeFlags::Script | ETypeFlags::Struct
    )
{
    Instancable=new CScriptStructInstance(this);
    Properties=new CGSharpPropertyList(this);
    GarbageCollectable=new CPropertyGarbageCollectable(this);
    Functions= new CFunctionList(this);

    PostConstructFunction= [this]()
    {
        OnPostConstruct(this);
    };
}

CGSharpStruct::~CGSharpStruct()
{
    if(StaticFieldValues)
    {
        delete StaticFieldValues;
    }
}

void CGSharpStruct::OnPostConstruct(CGSharpStruct * InStruct)
{
    assert(InStruct->Properties->IsScriptPropertyList());
    CGSharpPropertyList * GSharpFields = static_cast<CGSharpPropertyList*>(InStruct->Properties);
    InStruct->StaticFieldValues=GSharpFields->CreateStaticFieldValues();

    //invoke static constructor if exist
    for(auto & Function: InStruct->Functions->Functions)
    {
        if(Function->IsStatic() && Function->IsConstructor())
        {
            TVector<CAny> Args;
            Function->Invoke(Args);
            break;
        }
    }
}

CGSharpFieldValuesContainer *CGSharpStruct::GetStaticValues() const
{
    return StaticFieldValues;
}

CString CGSharpStruct::CScriptStructInstance::ToDebugString(const void *Instance) const
{
    return StructType->GetFullName() +U"//" + CString::CreateHex((int64_t)Instance, true);
}

int64_t CGSharpStruct::CScriptStructInstance::GetDebugNamedChildNum(const void *Instance) const
{ 
    return StructType->Properties->GetNonStaticFieldProperties().Num();
}

CType *CGSharpStruct::CScriptStructInstance::GetDebugNamedChildType(int64_t Index, const void *Instance) const
{ 
    return StructType->Properties->GetNonStaticFieldProperties()[Index]->GetType().BasicType;
}

CString CGSharpStruct::CScriptStructInstance::GetDebugNamedChildName(int64_t Index, const void *Instance) const
{ 
    return StructType->Properties->GetNonStaticFieldProperties()[Index]->GetName();
}

const void *CGSharpStruct::CScriptStructInstance::GetDebugNamedChildValue(int64_t Index, const void *Instance) const
{
    return StructType->Properties->GetNonStaticFieldProperties()[Index]->FieldProperty->GetValuePointer(Instance);
}

uint32_t CGSharpStruct::CScriptStructInstance::GetSize()
{
    return sizeof(CGSharpFieldValuesContainer);
}

uint64_t CGSharpStruct::CScriptStructInstance::GetInstanceHash(const void *Instance) const
{
    return StructType->Properties->GetNonStaticFieldHash(Instance);
}

bool CGSharpStruct::CScriptStructInstance::IsInstanceEqual(const void *Instance0, const void *Instance1) const
{
    return StructType->Properties->IsNonStaticFieldEqual(Instance0, Instance1);
}

void CGSharpStruct::CScriptStructInstance::InitInstance(void *Memory)
{
    // assume all non static fields are script fields
    TVector<CGSharpFieldProperty* > NonStaticFields;
    NonStaticFields.Reserve(StructType->Properties->GetNonStaticFieldProperties().Num());
    for(auto FieldProperty : StructType->Properties->GetNonStaticFieldProperties())
    {
        assert(FieldProperty->IsField() && FieldProperty->IsScript());
        CGSharpFieldProperty * GSharpField = reinterpret_cast<CGSharpFieldProperty*>(FieldProperty);
        NonStaticFields.Add(GSharpField);
    }

    CGSharpFieldValuesContainer * StructPtr 
        = new (Memory) CGSharpFieldValuesContainer(
            NonStaticFields
            ,RObject::StaticClass()->Properties->GetNonStaticFieldProperties().Num()
            );
}

void CGSharpStruct::CScriptStructInstance::EraseInstance(void *Instance)
{
    CGSharpFieldValuesContainer * StructPtr 
        = static_cast<CGSharpFieldValuesContainer*>(Instance);

    StructPtr->~CGSharpFieldValuesContainer();
}

void CGSharpStruct::CScriptStructInstance::CloneInstance(const void *Instance, void *TargetMemory)
{
    const CGSharpFieldValuesContainer * StructPtr 
        = static_cast<const CGSharpFieldValuesContainer*>(Instance);

    CGSharpFieldValuesContainer * NewStructPtr 
        = new (TargetMemory) CGSharpFieldValuesContainer(*StructPtr);
}

void CGSharpStruct::CScriptStructInstance::ResetAllocated(void *Memory)
{
    //convert to CGSharpFieldValuesContainer
    CGSharpFieldValuesContainer * StructPtr 
        = reinterpret_cast<CGSharpFieldValuesContainer*>(Memory);

    StructPtr->ResetValues();
}
